#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "util.hpp"
using namespace std;

#define NUM 1024

class TcpClient
{
public: 
    TcpClient(const string &serverip, const uint16_t &serverport)
        :_sockfd(-1), _serverip(serverip), _serverport(serverport)
    {}
    void initClient()
    {
        // 1.创建client的套接字
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd == -1)
        {
            //客户端出错也可以有日志, 例如向microsoft报告错误日志信息, 但今天我们不这么搞了就, 直接cerr输出错误信息
            cerr << "create socket error!" << endl;
            exit(2);
        }
        // 2.tcp的客户端需不需要bind?[必须要!], 需不需要显示的bind呢?[不需要, 由OS来bind随机分配ip和port](自己显示bind的话, 这个端口号很有可能发生冲突)

    }
    void start()
    {
        // 3.客户端要发起连接请求
        //之前在UDP的时候,首次sendto的时候OS会自动给client绑定ip和port,而connect调用成功时,会自动bind成功
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        
        int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
        if(n == -1) cerr << "socket connect error!" << endl;
        else
        {
            // string msg;
            while(true)
            {   
                int choice = 0;
                cout << "Please choose the function that you want achieve (upload - 1 or pull - 2) -> ";
                cin >> choice;
                getchar();
                
                write(_sockfd, &choice, sizeof(int));
                if(choice == 1) upload();
                else if(choice == 2) pull();
                
            }
        }
    }
    ~TcpClient(){if(_sockfd >= 0) close(_sockfd);}//关闭客户端的_sockfd, 其实没必要关闭, 因为client退出的时候, 他的fd资源会被OS回收掉
private:
    void pull()
    {
        //1.发送文件名
        string filename, tmp;
        cout << "Please Enter the filename searched : ";
        
        getline(cin, tmp);
        filename += "./clientLocal/" + tmp;
        file_util::write(filename, "");//创建文件
        
        write(_sockfd, tmp.c_str(), tmp.size());

        //2.读取文件大小
        char buffer[4];
        int n = read(_sockfd, buffer, 4);
        int32_t* valuePtr = reinterpret_cast<int32_t*>(buffer);
        
        int filesize = *valuePtr;
        
        string content;
        content.resize(filesize);
        //3.读取文件内容
        n = read(_sockfd, (void*)content.c_str(), content.size() + 1);
        cout << content << endl;
        file_util::write(filename, content);//向文件中进行写入
    }
    void upload()
    {
        //1.发送要上传的文件名字（服务器进行文件的创建）
        string filename;
        cout << "Please Enter the filename transmited : ";
        
        getline(cin, filename);
        write(_sockfd, filename.c_str(), filename.size());//你期望写的字节数
        cout << "提示信息: " << filename << endl;
        //2.发送要上传的文件大小（服务器进行缓冲区的准备）
        string body;
        file_util::read(filename, body);
        int size = body.size();
        write(_sockfd, &size, 4);//期望写入的字节数大小为4
        cout << "提示信息: " << size << endl;
        
        //3.发送要上传的文件内容（服务器进行文件内容的写入）
        write(_sockfd, body.c_str(), body.size());//期望写入的字节数大小就是文件内容的字节数
        cout << "提示信息: "<< body << endl;
        
    }
private:
    int _sockfd;
    string _serverip;//客户端得知道server的网络信息, 然后向server发送数据
    uint16_t _serverport;
};